﻿/**	\file		test_lxpref.cpp
 *	\date		(2014-11-24 20:10:43)/(2014-11-24 20:10:43)
 *-----------------------------------------------------------------------------
 *	\brief		测试动态分析
 *	\version	1.0.0.1
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#include	<lxtrial/lxtrial.h>
#include	<lolix/imp/lolix_plugin_auto_release_lib.h>
#include	<lolix/toy/loli_ptr.hpp>
#include	<lolix/toy/std_algorithm.hpp>
#include	<lolix/toy/std_list.hpp>
#include	<lxut/reg_pool.h>
#include	"../lxpref/lxpref.h"
#include	"../lxpref/lxpref_route.h"

using lolix::toy::loli_ptr;


double drand48(void);
void srand48(unsigned int i);


class pref_case
	: public lolix::itf_Tbase<pref_case, lxtrial::icase>
{
	LOLIX_DEFINE_ITF(pref_case, lxtrial::icase, (0x2f484596, 0xcffb48e4, 0x9daf379d, 0x3d2dccb8));
	lxtrial::probe* _prb;
	loli_ptr<lxpref::lxprofile> _prf;
	loli_ptr<lolix::loli::thread> _td_tst;
	loli_ptr<lolix::toy::time_info> _tinf;
	loli_ptr<lolix::dynamic_allocator> _alc;
	loli_ptr<lolix::loli::thread_factory> _td_fac;
	loli_ptr<lxpref::lxpref_fac>	_pref_fac;
	loli_ptr<lxpref::lxpref_route>		_pref_route;
	loli_ptr<lxpref::lxpref_route_end>	_pref_route_end;
	lolix::toy::list<lolix::lx_wchar const*> _str_list;
	lolix::lx_u64 _first_start_tick;
	lolix::lx_u64 _total_inner_time;

	bool _is_exits_td;
	pref_case(lolix::dynamic_allocator* alc);
	~pref_case(void);
public:
	static lolix::LOLIX_RETNV create_inst(base_type** out_ptr, lolix::dynamic_allocator* alc, lolix::ref_factory* ref_fac
		, lolix::toy::time_info::factory_type* tinf_fac
		, lolix::loli::thread_factory* td_fac
		, lxpref::lxpref_fac* pref_fac
		, lxpref::lxprofile* prf);
	LOLIX_INTERFACE_DEF(name, lolix::lx_wchar const*, (void)const);
	LOLIX_INTERFACE_DEF(test, lolix::LOLIX_RETNV, (lxut::reg_pool* reg, lxtrial::probe* prb));

private:
	void __Run_root(lxut::reg_pool* regp);
	void __Run_sort(lolix::size_type itemCnt);
	void __Run_stdsort(lolix::size_type itemCnt);
	void __Enter_1_to_10(void);
	void __Rand_dat(lolix::toy::list<lolix::lx_u32>& dat);
	void __Print_event_info(lxpref::event_info const* evt_inf);
	LOLIX_RETNV LOLIX_CALL __Start_peed_data(lolix::loli::thread* td);
};


pref_case::pref_case(lolix::dynamic_allocator* alc)
	: _str_list(alc)
	, _is_exits_td(false)
{
	lolix::imp::plugin_auto_release_lib_inc_ref();
}


pref_case::~pref_case(void)
{
	for ( auto iter = _str_list.begin(); iter != _str_list.end(); ++iter )
		_prf->free_string(*iter);
	lolix::imp::plugin_auto_release_lib_release();
}


lolix::LOLIX_RETNV
pref_case::create_inst(base_type** out_ptr
					, lolix::dynamic_allocator* alc
					, lolix::ref_factory* ref_fac
					, lolix::toy::time_info::factory_type* tinf_fac
					, lolix::loli::thread_factory* td_fac
					, lxpref::lxpref_fac* pref_fac
					, lxpref::lxprofile* prf)
{
	lolix::LOLIX_RETNV rtv;
	lolix::toy::time_info* tinf;
	if ( LOLIX_CHECK_ERROR(rtv = tinf_fac->create_inst(&tinf)) )
		return rtv;
	atexit_release(*tinf);

	//lxpref::lxpref_route* pref_route;
	//if ( LOLIX_CHECK_ERROR(rtv = pref_fac->create_inst(&pref_route, prf)) )
	//	return rtv;

	this_type* ptr;
	if ( LOLIX_CHECK_ERROR(rtv = __Create_inst_set_alc(&ptr, alc, ref_fac, alc)) )
		return rtv;
	ptr->_prf = prf;
	ptr->_td_fac = td_fac;
	ptr->_pref_fac = pref_fac;
	//ptr->_pref_route = pref_route;
	ptr->_tinf = tinf;
	*out_ptr = ptr;
	return rtv;
}


lolix::lx_wchar const*
pref_case::name(void)const
{
	return L"test/test_profile";
}

#include	<Windows.h>
lolix::LOLIX_RETNV
pref_case::test(lxut::reg_pool* regp, lxtrial::probe* prb)
{
	this->_prb = prb;
	lolix::LOLIX_RETNV rtv;

	lxpref::lxpref_route* pref_route;
	if ( LOLIX_CHECK_ERROR(rtv = this->_pref_fac->create_inst(&pref_route, this->_prf)) )
		return rtv;
	atexit_release(*pref_route);
	atexit_invoke(*pref_route, end());
	lolix::lx_wchar const* bind_path = L"127.0.0.1:7654";
	lolix::size_type bind_path_len = 14;
	if ( LOLIX_CHECK_ERROR(rtv = pref_route->start(bind_path, bind_path_len, 0)) )
		return rtv;

	lxpref::lxpref_route_end* pref_route_end;
	if ( LOLIX_CHECK_ERROR(rtv = this->_pref_fac->create_inst(&pref_route_end, bind_path, bind_path_len, 0)) )
		return rtv;
	atexit_release(*pref_route_end);
	atexit_invoke(*pref_route_end, close());

	this->_pref_route = pref_route;
	this->_pref_route_end = pref_route_end;

	loli_ptr<lolix::loli::thread> td;
	if ( LOLIX_CHECK_ERROR(rtv = this->_td_fac->create_inst(&td)) )
		return rtv;
	lolix::loli::thread::FN_INVOKE_PROC td_call(this, &pref_case::__Start_peed_data);
	if ( LOLIX_CHECK_ERROR(rtv = td->start(td->THREAD_START_NORMAL, td_call, true)) )
		return rtv;

	Sleep(100);
	__Enter_1_to_10();
	for ( size_type i = 0; i != 16; ++i )
		this->__Run_root(regp);
	__Enter_1_to_10();
	lolix::loli::signal* sign;
	if ( LOLIX_CHECK_ERROR(rtv = this->_prf->query_signal(&sign)) )
		return rtv;

	lolix::toy::time_span ts(this->_tinf);
	ts.set_time(1, lolix::toy::time_span::s<int>());
	if ( !LOLIX_IS_ERROR(_td_fac->local_thread(&td)) )
		td->sleep(&ts);
	Sleep(100);
	_is_exits_td = true;
	sign->set();
	sign->release();
	td->wait(0, lolix::toy::time_span::time_max());
	return lolix::LOLIX_RETNV_OK;
}


void
pref_case::__Run_root(lxut::reg_pool* regp)
{
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Run_root");
	atexit_invoke(*this->_prf, leave());

	lolix::lx_u32 total_cnt = KB(4);
	lolix::lx_u32 chk_counter = 64;
	regp->get_uint(&total_cnt, L"total_cnt");
	regp->get_uint(&chk_counter, L"sub_len");
	for ( int i = 0; i != total_cnt; ++i )
	{
		if ( i & 1 )
			this->__Run_sort(chk_counter);
		else
			this->__Run_stdsort(chk_counter);
	}
}


void
pref_case::__Run_sort(lolix::size_type itemCnt)
{
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Run_sort");
	atexit_invoke(*this->_prf, leave());

	lolix::toy::list<lolix::lx_u32> dat(_alc);
	for ( lolix::lx_u32 i = 0; i != itemCnt; ++i )
		dat.push_back(i);
	__Rand_dat(dat);
	dat.sort();
}


void
pref_case::__Run_stdsort(lolix::size_type itemCnt)
{
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Run_stdsort");
	atexit_invoke(*this->_prf, leave());

	lolix::toy::list<lolix::lx_u32> dat(_alc);
	for ( lolix::lx_u32 i = 0; i != itemCnt; ++i )
		dat.push_back(i);
	__Rand_dat(dat);
	lolix::toy::sort(dat.begin(), dat.end());
}


void
pref_case::__Enter_1_to_10(void)
{
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_1");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_2");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_3");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_4");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_5");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_6");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_7");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_8");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_9");
	this->_prf->enter(lxpref::EVENT_FLAG_BG, L"pref_case::__Enter_10");
	for ( lolix::size_type i = 0; i != 10; ++i )
		this->_prf->leave();
}


void
pref_case::__Rand_dat(lolix::toy::list<lolix::lx_u32>& dat)
{
	for ( auto iter = dat.begin(); iter != dat.end(); )
	{
		if ( drand48() < 0.5 )
		{
			 ++iter;
			 continue;
		}
		dat.push_back(*iter);
		iter = dat.erase(iter);
	}
}


lolix::LOLIX_RETNV
pref_case::__Start_peed_data(lolix::loli::thread* td)
{
	lolix::loli::signal* sign;
	lolix::LOLIX_RETNV rtv;
	if ( LOLIX_CHECK_ERROR(rtv = this->_prf->query_signal(&sign)) )
		return rtv;
	atexit_release(*sign);
	_first_start_tick = 0;

	lolix::toy::time_span ts(this->_tinf);
	ts.set_time(1, lolix::toy::time_span::s<int>());
	do
	{
		if ( LOLIX_CHECK_ERROR(rtv = this->_pref_route_end->wait(&ts)) )
			return rtv;

		lxpref::event_info const* evt_info;
		while ( !LOLIX_CHECK_ERROR(this->_pref_route_end->top(&evt_info)) )
		{
			if ( !evt_info )
				break;
			if ( !_first_start_tick )
			{
				_first_start_tick = evt_info->enter_time_tick;
				_total_inner_time = 0;
			}

			this->__Print_event_info(evt_info);
			this->_pref_route_end->fetch(evt_info);
		}
	}while(!this->_is_exits_td);
	return lolix::LOLIX_RETNV_OK;
}


void
pref_case::__Print_event_info(lxpref::event_info const* in_evt_inf)
{
	lxpref::event_info evt_buf(*in_evt_inf);
	auto evt_inf = &evt_buf;
	if ( !evt_inf->leave_time_tick )
		evt_inf->leave_time_tick = evt_inf->enter_time_tick;
	lolix::lx_u64 self_time = evt_inf->leave_time_tick - evt_inf->enter_time_tick;
	_total_inner_time += self_time;
	double p = (double)_total_inner_time/(double)(evt_inf->leave_time_tick - this->_first_start_tick);

	if ( evt_inf->section_type == lxpref::EVENT_FLAG_ED )
		this->_prb->output(this->_prb->OUT_TYPE_INFO, "%I64u(%I64u:%f):RET", evt_inf->enter_time_tick, self_time, p);
	else
		this->_prb->output(this->_prb->OUT_TYPE_INFO, L"%I64u(%I64u:%f):%s", evt_inf->enter_time_tick, self_time, p, evt_inf->str_param[0]);
}


lolix::LOLIX_RETNV
__CreateLxPrefCase(lxtrial::icase** out_ptr
					, lolix::dynamic_allocator* alc
					, lolix::ref_factory* ref_fac
					, lolix::toy::time_info::factory_type* tinf_fac
					, lolix::loli::thread_factory* td_fac
					, lxpref::lxpref_fac* pref_fac
					, lxpref::lxprofile* pref)
{
	return pref_case::create_inst(out_ptr, alc, ref_fac, tinf_fac, td_fac, pref_fac, pref);
}


long long mul(long long a, long long b)
{
	lolix::lx_u16 const* va = (lolix::lx_u16 const*)&a;
	lolix::lx_u16 const* vb = (lolix::lx_u16 const*)&b;
	lolix::lx_u16 buf[8] = {0};
	for ( unsigned int x = 0; x != 4; ++x )
		for ( unsigned int y = 0; y != 4; ++y )
		{
			lolix::lx_u32 ra = va[x];
			lolix::lx_u32 rb = vb[y];
			lolix::lx_u32 rr = ra * rb;
			if ( (lolix::lx_u32)buf[x+y] + (rr & 0xffff) > 0xffff )
				rr += 0x10000;
			buf[x + y] = ((rr + buf[x + y]) & 0xffff);
			if ( (lolix::lx_u32)buf[x+y+1] + (rr>>16) > 0xffff )
			{
				if ( buf[x+y+2] == 0xffff )
					++buf[x + y + 3];
				++buf[x + y + 2];
			}
			buf[x + y + 1] = (buf[x + y + 1] + (rr >> 16)) & 0xffff;
		}
	lolix::lx_u64 const* p = (lolix::lx_u64 const*)buf;
	LOLIX_ASSERT(*p == a * b, "mul64 error");
	return *p;
}

#define m 0x100000000LL
#define c 0xB16
#define a 0x5DEECE66DLL

static unsigned long long seed = 1;


double drand48(void)
{
	seed = (mul(a, seed ) + c) & 0xFFFFFFFFFFFFLL;
	unsigned int x = seed >> 16;
    return 	((double)x / (double)m);

}

void srand48(unsigned int i)
{
    seed  = (((long long int)i) << 16);
}
